﻿//
// © Algobox Ltd - Benn Eichhorn
//
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Collections.Concurrent;
using Algobox.Objects.Finance.Assets.Stock;
using Algobox.Objects.Finance;
using Algobox.Toolbox.Collections;
using Algobox.Structure.Finance.Strategies.SmartStoploss;
using System.Windows.Data;
using System.Collections.ObjectModel;
using System.Xml.Linq;

namespace Algobox.Structure.Finance.Strategies.FractionPrototype
{
    public class FractionManagement
    {
        #region Constructors

        public FractionManagement(Algobox.Feeds.Finance.MarketData.IMarketFeedAdaptor marketFeed, IFractionData data)
        {
            MarketFeed = marketFeed;
            Data = data;
        }

        static FractionManagement()
        {
            OpenStrategies = new ConcurrentObservableDictionary<Guid, FractionComponentOpen>();
            DayStrategies = new ConcurrentObservableDictionary<Guid, FractionComponentDay>();
            DayStrategiesMonitor = new ConcurrentObservableDictionary<string, FractionComponentDayMonitor>();
            Triggers = new ConcurrentObservableDictionary<Guid, FractionTrigger>();
        }

        #endregion
//////////////////
        #region Member Variables
        
        /// <summary>
        /// Logger used by this class
        /// </summary>        
        private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();

        public Algobox.Feeds.Finance.MarketData.IMarketFeedAdaptor MarketFeed { get; set; }
        
        #endregion
//////////////////
        #region Properties

        public bool IsLoaded { get; private set; }
        public bool IsStarted { get; private set; }

        public static ConcurrentObservableDictionary<Guid, FractionComponentOpen> OpenStrategies { get; set; }
        public static ConcurrentObservableDictionary<Guid, FractionComponentDay> DayStrategies { get; set; }
        public static ConcurrentObservableDictionary<string, FractionComponentDayMonitor> DayStrategiesMonitor { get; set; }
        public static ConcurrentObservableDictionary<Guid, FractionTrigger> Triggers { get; set; }
        public IFractionData Data { get; private set; }

        #endregion
//////////////////
        #region Functions

        public FractionComponentOpen AddOrUpdateOpen(
            Guid id,
            string symbolA,
            string symbolB)
        {
            symbolA = symbolA.ToUpperInvariant();
            symbolB = symbolB.ToUpperInvariant();

            // create the view if we are not using a base view created elsewhere
            if (OpenStrategies == null)
            {
                OpenStrategies = new ConcurrentObservableDictionary<Guid, FractionComponentOpen>();
            }
            Log.Info("Creating FractionComponentOpen with {0} and {1}", symbolA, symbolB);

            // create or update the strategy and add it to the manager dictionary
            FractionComponentOpen strategy = OpenStrategies.AddOrUpdate(id,
                (key) =>
                {
                    FractionComponentOpen innerStrategy = new FractionComponentOpen(
                        id,
                        this,
                        Data.AddStock(symbolA),
                        Data.AddStock(symbolB)
                        );

                    return innerStrategy;
                },
                (key, innerStrategy) =>
                {
                    if (id == innerStrategy.DatabaseId)
                    {

                    }
                    return innerStrategy;
                });

            return strategy;
        }

        public FractionComponentDay AddOrUpdateDay(
            Guid id,
            string symbolA,
            string symbolB)
        {
            // create the view if we are not using a base view created elsewhere
            if (DayStrategies == null)
            {
                DayStrategies = new ConcurrentObservableDictionary<Guid, FractionComponentDay>();
            }
            if (DayStrategiesMonitor == null)
            {
                DayStrategiesMonitor = new ConcurrentObservableDictionary<string, FractionComponentDayMonitor>();
            }
            Log.Info("Creating FractionComponentDay with {0} and {1}", symbolA, symbolB);

            // create or update the strategy and add it to the manager dictionary
            FractionComponentDay strategy = DayStrategies.AddOrUpdate(id,
                (key) =>
                {
                    FractionComponentDay innerStrategy = new FractionComponentDay(
                        id,
                        this,
                        Data.AddStock(symbolA),
                        Data.AddStock(symbolB)
                        );

                    return innerStrategy;
                },
                (key, innerStrategy) =>
                {
                    if (id == innerStrategy.DatabaseId)
                    {
                        
                    }
                    return innerStrategy;
                });

            string monitorId = symbolA + "|" + symbolB;
            if (!DayStrategiesMonitor.ContainsKey(monitorId))
            {
                FractionComponentDayMonitor monitor = new FractionComponentDayMonitor(this, strategy);
                DayStrategiesMonitor.AddOrUpdate(monitorId, monitor);
            }

            return strategy;
        }

        public bool TryAddTrigger(Guid id, FractionTrigger trigger)
        {
            return Triggers.TryAdd(id, trigger);
        }

        public void Remove(FractionComponentOpen strategy)
        {
            MarketFeed.UnsubscribeLast(strategy.Security, strategy);
            MarketFeed.UnsubscribeLast(strategy.SecurityPair, strategy);
            MarketFeed.UnsubscribeOpen(strategy.Security, strategy);
            MarketFeed.UnsubscribeOpen(strategy.SecurityPair, strategy);

            OpenStrategies.TryRemove(strategy.DatabaseId);
        }

        public void Remove(FractionComponentDay strategy)
        {
            MarketFeed.UnsubscribeLast(strategy.Security, strategy);
            MarketFeed.UnsubscribeLast(strategy.SecurityPair, strategy);

            DayStrategies.TryRemove(strategy.DatabaseId);

            string key = strategy.SymbolA + "|" + strategy.SymbolB;
            if (DayStrategies.Values.Count(s => s.SymbolA == strategy.SymbolA && s.SymbolB == strategy.SymbolB) > 0)
            {
                MarketFeed.UnsubscribeLast(strategy.Security, strategy);
                MarketFeed.UnsubscribeLast(strategy.SecurityPair, strategy);

                DayStrategiesMonitor.TryRemove(strategy.SymbolA + "|" + strategy.SymbolB);
            }
        }

        public void RemoveAll()
        {
            foreach (KeyValuePair<Guid, FractionComponentOpen> strategy in OpenStrategies)
            {
                MarketFeed.UnsubscribeLast(strategy.Value.Security, strategy.Value);
                MarketFeed.UnsubscribeLast(strategy.Value.SecurityPair, strategy.Value);
                MarketFeed.UnsubscribeOpen(strategy.Value.Security, strategy.Value);
                MarketFeed.UnsubscribeOpen(strategy.Value.SecurityPair, strategy.Value);
            }
            foreach (KeyValuePair<Guid, FractionComponentDay> strategy in DayStrategies)
            {
                MarketFeed.UnsubscribeLast(strategy.Value.Security, strategy.Value);
                MarketFeed.UnsubscribeLast(strategy.Value.SecurityPair, strategy.Value);
            }
            foreach (KeyValuePair<string, FractionComponentDayMonitor> strategy in DayStrategiesMonitor)
            {
                strategy.Value.Unsubscribe();
            }
            OpenStrategies.Clear();
            DayStrategies.Clear();
            DayStrategiesMonitor.Clear();
        }


        public XElement SaveConfig()
        {
            XElement xml = new XElement(this.GetType().Name);

            foreach (FractionComponentOpen item in 
                OpenStrategies.Values.OrderBy(s => s.SymbolA).OrderBy( s => s.SymbolB).OrderBy(s => s.Trigger))
            {
                xml.Add(
                    new XElement(typeof(FractionComponentOpen).Name,
                        new XElement("SymbolA", item.SymbolA),
                        new XElement("SymbolB", item.SymbolB),
                        new XElement("Value", item.Value),
                        new XElement("Trigger", item.Trigger),
                        new XElement("Exit", item.Exit)
                        )
                    );
            }
            foreach (FractionComponentDay item in
                DayStrategies.Values.OrderBy(s => s.SymbolA).OrderBy(s => s.SymbolB).OrderBy(s => s.Trigger))
            {
                xml.Add(
                    new XElement(typeof(FractionComponentDay).Name,
                        new XElement("SymbolA", item.SymbolA),
                        new XElement("SymbolB", item.SymbolB),
                        new XElement("Value", item.Value),
                        new XElement("Trigger", item.Trigger),
                        new XElement("Exit", item.Exit)
                        )
                    );
            }
            return xml;
        }

        public void LoadConfig(XElement xml)
        {
            RemoveAll();
            foreach(XElement strategy in xml.Descendants(typeof(FractionComponentDay).Name))
            {
                try
                {
                    string symbolA = (string)strategy.Element("SymbolA");
                    string symbolB = (string)strategy.Element("SymbolB");

                    FractionComponentDay item =
                        AddOrUpdateDay(Guid.NewGuid(), symbolA, symbolB);

                    ushort u;
                    if (UInt16.TryParse((string)strategy.Element("Value"), out u))
                        item.Value = u;

                    double d;
                    if (Double.TryParse((string)strategy.Element("Trigger"), out d))
                        item.Trigger = d;
                    
                    if (Double.TryParse((string)strategy.Element("Exit"), out d))
                        item.Exit = d;

                }
                catch
                {
                    Log.Warn("Invalid FractionComponentDay read from xml: " + strategy);
                }                
            }
            foreach (XElement strategy in xml.Descendants(typeof(FractionComponentOpen).Name))
            {
                try
                {
                    string symbolA = (string)strategy.Element("SymbolA");
                    string symbolB = (string)strategy.Element("SymbolB");

                    FractionComponentOpen item =
                        AddOrUpdateOpen(Guid.NewGuid(), symbolA, symbolB);

                    ushort u;
                    if (UInt16.TryParse((string)strategy.Element("Value"), out u))
                        item.Value = u;

                    double d;
                    if (Double.TryParse((string)strategy.Element("Trigger"), out d))
                        item.Trigger = d;

                    if (Double.TryParse((string)strategy.Element("Exit"), out d))
                        item.Exit = d;

                }
                catch
                {
                    Log.Warn("Invalid FractionComponentOpen read from xml: " + strategy);
                }
            }
        }

        #endregion
//////////////////
        #region Enums


        #endregion


    }
}